﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ScannerClasses
{
   public class Scanner :IScanner
    {
       private string _filepath;
       private StreamReader _filereader;
       private List<IToken> _tokens;
       private int _currentTokenID;

       public Scanner(string filePath)
       {
           _filepath = filePath;
           _tokens = new List<IToken>();
           _currentTokenID = 0;
           try
           {
               _filereader = new StreamReader(@_filepath);
           }
           catch(ArgumentException e){
               Console.WriteLine("That was an empty string bitch!\r\n" + e.ToString());
              // Environment.Exit(1);
           }catch(FileNotFoundException e){
               Console.WriteLine("Point me to the file bitch!\r\n"+e.ToString());
            //   Environment.Exit(1);
           }
           Task.Run(() => ListBuilder());
        }

       private char GetSource()
       {
           char EOT = '\0';
          int cc =  _filereader.Read();
          if (cc < 0)
          {
              return EOT;
          }
          else
          {
              return (char)cc;
          }
       }

       private char PeekSource()
       {
           char EOT = '\0';
           int cc = _filereader.Peek();
           if (cc < 0)
           {
               return EOT;
           }
           else
           {
               return (char)cc;
           }
       }

       private void TakeChar(ref StringBuilder currentSpelling, ref char currentchar, ref char nextchar){

           currentSpelling.Append(currentchar);
           currentchar = GetSource();
           nextchar = PeekSource();
       }
       private void IgnoreChar(ref StringBuilder currentSpelling, ref char currentchar, ref char nextchar)
       {

           currentchar = GetSource();
           nextchar = PeekSource();
       }

       private void scanSeparator(ref StringBuilder currentSpelling, ref char currentchar, ref char nextchar)
       {

           if (currentchar == '-' && nextchar == '-')
           {
              TakeChar(ref  currentSpelling, ref  currentchar, ref  nextchar);
              while (currentchar != '\n' && currentchar != '\0') TakeChar(ref  currentSpelling, ref  currentchar, ref  nextchar);

              if (currentchar == '\n') TakeChar(ref  currentSpelling, ref  currentchar, ref  nextchar);

           }
           else if (currentchar == '-' && nextchar == '*')
           {
               TakeChar(ref  currentSpelling, ref  currentchar, ref  nextchar);
               while (!(currentchar == '*' && nextchar == '-')) TakeChar(ref  currentSpelling, ref  currentchar, ref  nextchar);
               TakeChar(ref  currentSpelling, ref  currentchar, ref  nextchar);
               TakeChar(ref  currentSpelling, ref  currentchar, ref  nextchar);
           }else if(currentchar == ' ' || currentchar == '\r' || currentchar == '\n' ||currentchar == '\t' ){
               TakeChar(ref  currentSpelling, ref  currentchar, ref  nextchar);
           }

       }

       private bool IsLetter(char c)
       {
           return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z';
       }


       private bool IsDigit(char c)
       {
           return c >= '0' && c <= '9';
       }
       //WORDLITERAL,
       // SEMICOLON,
       //COMMA, LEFTPARAN, RIGHTPARAN, LEFTCURLYBRACE, RIGHTCURLYBRACE,ERROR,ENDOFFILE};
       private TokenTypes.TokenType ScanTokenType(ref StringBuilder currentSpelling, ref char currentchar, ref char nextchar)
       {
          if(IsLetter(currentchar)){
              TakeChar(ref currentSpelling, ref currentchar, ref nextchar);
              while(IsLetter(currentchar) || IsDigit(currentchar)){
                    TakeChar(ref currentSpelling, ref currentchar, ref nextchar);
              }
              return TokenTypes.TokenType.IDENTIFIER;
          }else if(IsDigit(currentchar)){

              TakeChar(ref currentSpelling, ref currentchar, ref nextchar);
              while (IsDigit(currentchar))
              {
                  TakeChar(ref currentSpelling, ref currentchar, ref nextchar);
              }
              if( currentchar =='.'){
                  TakeChar(ref currentSpelling, ref currentchar, ref nextchar);
                  while (IsDigit(currentchar))
                  {
                      TakeChar(ref currentSpelling, ref currentchar, ref nextchar);
                  }
                  return TokenTypes.TokenType.DOUBLELITERAL;
              }
              return TokenTypes.TokenType.INTEGERLITERAL;
          }else if(currentchar =='+' ||currentchar =='-' ||currentchar =='*' ||currentchar =='/' ||currentchar =='%'  ||currentchar =='=' ||currentchar =='&' ||currentchar =='|' ||currentchar =='!' ){
               TakeChar(ref currentSpelling, ref currentchar, ref nextchar);
              return TokenTypes.TokenType.OPERATOR;
          }else if(currentchar ==','){
               TakeChar(ref currentSpelling, ref currentchar, ref nextchar);
              return TokenTypes.TokenType.COMMA;
          }else if(currentchar ==';'){
               TakeChar(ref currentSpelling, ref currentchar, ref nextchar);
              return TokenTypes.TokenType.SEMICOLON;
          }else if(currentchar =='('){
               TakeChar(ref currentSpelling, ref currentchar, ref nextchar);
              return TokenTypes.TokenType.LEFTPARAN;
          }else if(currentchar ==')'){
               TakeChar(ref currentSpelling, ref currentchar, ref nextchar);
              return TokenTypes.TokenType.RIGHTPARAN;
          }else if(currentchar =='{'){
               TakeChar(ref currentSpelling, ref currentchar, ref nextchar);
              return TokenTypes.TokenType.LEFTCURLYBRACE;
          }else if (currentchar == '.')
          {
              TakeChar(ref currentSpelling, ref currentchar, ref nextchar);
              return TokenTypes.TokenType.DOT;
          }else if(currentchar == '}')
          {
              TakeChar(ref currentSpelling, ref currentchar, ref nextchar);
              return TokenTypes.TokenType.RIGHTCURLYBRACE;
          }
          else if (currentchar == '"')
          {
              IgnoreChar(ref currentSpelling, ref currentchar, ref nextchar);
              while (currentchar != '"')
              {
                  TakeChar(ref currentSpelling, ref currentchar, ref nextchar);
              }
              IgnoreChar(ref currentSpelling, ref currentchar, ref nextchar);
              return TokenTypes.TokenType.WORDLITERAL;
          }
          else if(currentchar == '\0')
          {
              TakeChar(ref currentSpelling, ref currentchar, ref nextchar);
              return TokenTypes.TokenType.ENDOFFILE;
          }
          else
          {
              TakeChar(ref currentSpelling, ref currentchar, ref nextchar);
              return TokenTypes.TokenType.ERROR;
          }

       }

       private void ListBuilder()
       {
           char currentchar = GetSource();
           char nextchar = PeekSource();
           IToken t = TokenBuilder(ref currentchar, ref nextchar);
           while (t.Type != TokenTypes.TokenType.ENDOFFILE)
           {
               _tokens.Add(t);
               t = TokenBuilder(ref currentchar, ref nextchar);
           }
       }

       private Token TokenBuilder(ref char currentchar, ref char nextchar)
       {

           StringBuilder sb = new StringBuilder();
           while ((currentchar == '-' && nextchar == '-') || (currentchar == '-' && nextchar == '*') || currentchar == '\n' || currentchar == '\r' || currentchar == '\t' || currentchar == ' ')
           {
               scanSeparator(ref sb, ref currentchar, ref nextchar);
           }
           sb = new StringBuilder();
           TokenTypes.TokenType v = ScanTokenType(ref sb, ref currentchar, ref nextchar);
           
           return new Token(v,sb.ToString());
       }

        public IToken Scan()
        {
            if (HasNext())
            {
                IToken t = _tokens.ElementAt(_currentTokenID);
                _currentTokenID++;
                return t;
            }
            else
            {
                return null;
            }
        }

        public bool HasNext()
        {
            return (_tokens.Count) > _currentTokenID;
        }


        public bool HasToken(TokenTypes.TokenType m)
        {
            return _tokens.Any(x => x.Type == m);
        }


        public IToken Peek()
        {
            if (HasNext())
            {
                IToken t = _tokens.ElementAt(_currentTokenID);
                return t;
            }
            else
            {
                return null;
            }
        }
    }
}
